/*
 * Copyright 2011-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0.
 * See `LICENSE` in the project root for license information.
 */

package me.ijleex.mgmt.codegen.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import org.apache.velocity.VelocityContext;

import com.google.common.base.CaseFormat;
import com.google.common.base.Strings;

import me.ijleex.mgmt.codegen.entity.ColumnInfo;
import me.ijleex.mgmt.codegen.entity.TableInfo;
import me.ijleex.mgmt.commons.util.DateUtil;

/**
 * 代码生成 工具
 *
 * @author liym
 * @since 2019-02-26 12:36 新建
 */
public final class CodeGenUtil {

    /**
     * SQL 数据类型 与 Java 类型对应关系
     */
    private static final Map<String, String> JAVA_TYPE_MAP = new HashMap<>(32);

    /** MyBatis 空间路径 */
    private static final String MYBATIS_MAPPER_ROOT = "main/resources/mapper";

    /** HTML 空间路径 */
    private static final String HTML_PATH = "main/resources/html";

    /**
     * 下划线
     */
    private static final String UNDERSCORE = "_";

    /**
     * 表名转换成Java类名
     */
    public static String tableNameToClassName(String tableName, String tablePrefix) {
        if (!Strings.isNullOrEmpty(tablePrefix)) {
            tableName = tableName.replaceFirst(tablePrefix, "");
        }
        return toUpperCamelCase(tableName);
    }

    /**
     * 转换列信息
     *
     * @param columns 表的列信息
     * @return 处理后的表的列信息
     * @since 2019-02-26 17:43
     */
    public static List<ColumnInfo> transferColumns(List<ColumnInfo> columns) {
        // 列信息
        List<ColumnInfo> columnsList = new ArrayList<>(columns.size());
        for (ColumnInfo column : columns) {
            // 列名转换成 Java属性名
            String fieldName = toLowerCamelCase(column.getColumnName());
            column.setFieldName(fieldName);
            column.setFieldCapsName(toUpperCamelCase(fieldName));
            column.setExtra(column.getExtra());

            // 列的数据类型，转换成Java类型
            String fieldType = JAVA_TYPE_MAP.get(column.getDataType());
            column.setFieldType(fieldType);

            columnsList.add(column);
        }
        return columnsList;
    }

    /**
     * 从表的列中查找主键
     *
     * @param columns 表的列信息
     * @return 主键列信息
     * @since 2019-02-26 22:21
     */
    public static ColumnInfo detectPrimaryKey(List<ColumnInfo> columns) {
        ColumnInfo pk = null;
        for (ColumnInfo column : columns) {
            String columnKey = column.getColumnKey();
            if ("PRI".equals(columnKey)) {
                pk = column;
                break;
            }
        }
        // 如果没有定义（找到）主键，则返回第一列
        // if (pk == null) {
        //     pk = columns.get(0);
        // }
        return pk;
    }

    /**
     * 获取模板信息
     *
     * @return 模板列表
     */
    public static VelocityContext getVelocityContext(TableInfo table, String basicPackage, String moduleName, String author) {
        // Java对象数据传递到模板文件vm
        VelocityContext context = new VelocityContext();

        context.put("author", author);
        context.put("datetime", DateUtil.getDatetime());

        context.put("basePackage", basicPackage);
        context.put("moduleName", moduleName);
        context.put("modulePackage", basicPackage + '.' + moduleName);

        context.put("className", table.getClassName());
        context.put("classVarName", table.getClassVarName());

        context.put("tableName", table.getTableName());
        context.put("columns", table.getColumns());
        context.put("primaryKey", table.getPrimaryKey());
        context.put("tableComment", replaceKeyword(table.getTableComment()));
        return context;
    }

    /**
     * @param keyword
     * @return
     * @since 2019-02-26 22:24
     */
    public static String replaceKeyword(String keyword) {
        return keyword.replaceAll("(?:表|信息)", "");
    }

    /**
     * 获取模板信息
     *
     * @return 模板列表
     */
    public static List<String> getTemplates() {
        List<String> templates = new ArrayList<>(10);
        templates.add("templates/java/Entity.java.vm");
        templates.add("templates/java/Mapper.java.vm");
        templates.add("templates/java/Service.java.vm");
        templates.add("templates/java/ServiceImpl.java.vm");
        templates.add("templates/java/Action.java.vm");
        templates.add("templates/java/package-info.java.vm");
        templates.add("templates/mapper/Mapper.xml.vm");
        templates.add("templates/html/index.html.vm");
        templates.add("templates/html/add.html.vm");
        templates.add("templates/html/edit.html.vm");
        templates.add("templates/menu/menu.sql.vm");
        return templates;
    }

    /**
     * 获取文件名
     */
    public static String getFileName(String template, TableInfo table, String moduleName) {
        // 大写类名
        String className = table.getClassName();
        // 小写类名
        String classVarName = table.getClassVarName();

        String javaModulePath = getProjectPath(moduleName);
        String mybatisPath = MYBATIS_MAPPER_ROOT + "/" + moduleName + "/" + className;
        String htmlPath = HTML_PATH + "/" + moduleName + "/" + classVarName;

        if (template.contains("Entity.java.vm")) {
            return javaModulePath + "entity" + "/" + className + ".java";
        }
        if (template.contains("Mapper.java.vm")) {
            return javaModulePath + "mapper" + "/" + className + "Mapper.java";
        }
        if (template.contains("Service.java.vm")) {
            return javaModulePath + "service" + "/" + "I" + className + "Service.java";
        }
        if (template.contains("ServiceImpl.java.vm")) {
            return javaModulePath + "service" + "/impl/" + className + "ServiceImpl.java";
        }
        if (template.contains("Action.java.vm")) {
            return javaModulePath + "action" + "/" + className + "Action.java";
        }
        if (template.contains("package-info.java.vm")) {
            return javaModulePath + "package-info.java";
        }

        if (template.contains("Mapper.xml.vm")) {
            return mybatisPath + "Mapper.xml";
        }

        if (template.contains("index.html.vm")) {
            return htmlPath + "/index.html";
        }
        if (template.contains("add.html.vm")) {
            return htmlPath + "/add.html";
        }
        if (template.contains("edit.html.vm")) {
            return htmlPath + "/edit.html";
        }
        if (template.contains("menu.sql.vm")) {
            return classVarName + "-menu.sql";
        }
        return "---";
    }

    public static String getProjectPath(String moduleName) {
        String packageName = "me/ijleex/mgmt/" + moduleName + "/";
        return "main/java/" + packageName;
    }

    /**
     * 转换为小写驼峰命名的字符串
     * <p/>
     * <p>LOWER_CAMEL -> lowerCamel</p>
     * <p>lower_camel -> lowerCamel</p>
     * <p>UpperCamel -> lowerCamel</p>
     *
     * @param name 下划线分隔命名的字符串
     * @return 小驼峰式命名的字符串
     */
    public static String toLowerCamelCase(String name) {
        if (Strings.isNullOrEmpty(name)) {
            return "";
        }
        if (name.contains(UNDERSCORE)) {
            return CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, name);
        } else {
            return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, name);
        }
    }

    /**
     * 转换为大写驼峰命名的字符串
     * <p/>
     * <p>UPPER_CAMEL -> UpperCamel</p>
     * <p>upper_camel -> UpperCamel</p>
     * <p>lowerCamel -> UpperCamel</p>
     *
     * @param name 下划线分隔命名的字符串
     * @return 大写驼峰命名的字符串
     * @since 2019-03-02 13:52
     */
    public static String toUpperCamelCase(String name) {
        if (Strings.isNullOrEmpty(name)) {
            return "";
        }
        if (name.contains(UNDERSCORE)) {
            return CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, name);
        } else {
            return CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, name);
        }
    }

    /**
     * 将下划线分隔的字符串转换为连接符分隔的字符串
     * <p/>
     * <p>LOWER_HYPHEN -> lower-hyphen</p>
     *
     * @param name 下划线分隔命名的字符串
     * @return 连接符分隔的字符串，用于 HTML 中
     * @since 2019-03-02 14:03
     */
    public static String toLowerHyphen(String name) {
        if (Strings.isNullOrEmpty(name)) {
            return "";
        }
        name = name.toLowerCase(Locale.ENGLISH);
        return CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_HYPHEN, name);
    }

    static {
        // https://blog.csdn.net/qq_32711309/article/details/80003388
        // https://blog.csdn.net/qq_32711309/article/details/80003388
        JAVA_TYPE_MAP.put("tinyint", "Integer");
        JAVA_TYPE_MAP.put("smallint", "Integer");
        JAVA_TYPE_MAP.put("mediumint", "Integer");
        JAVA_TYPE_MAP.put("int", "Integer");
        JAVA_TYPE_MAP.put("number", "Integer");
        JAVA_TYPE_MAP.put("integer", "Integer");
        JAVA_TYPE_MAP.put("bigint", "Long");
        JAVA_TYPE_MAP.put("float", "Float");
        JAVA_TYPE_MAP.put("double", "Double");
        JAVA_TYPE_MAP.put("decimal", "BigDecimal");
        JAVA_TYPE_MAP.put("bit", "Boolean");
        JAVA_TYPE_MAP.put("char", "String");
        JAVA_TYPE_MAP.put("varchar", "String");
        JAVA_TYPE_MAP.put("varchar2", "String");
        JAVA_TYPE_MAP.put("tinytext", "String");
        JAVA_TYPE_MAP.put("text", "String");
        JAVA_TYPE_MAP.put("mediumtext", "String");
        JAVA_TYPE_MAP.put("longtext", "String");
        JAVA_TYPE_MAP.put("time", "LocalTime");
        JAVA_TYPE_MAP.put("date", "LocalDate");
        JAVA_TYPE_MAP.put("datetime", "LocalDateTime");
        JAVA_TYPE_MAP.put("timestamp", "LocalDateTime");

        JAVA_TYPE_MAP.put("blob", "Object");
        JAVA_TYPE_MAP.put("clob", "Object");
        JAVA_TYPE_MAP.put("binary", "byte[]");
        JAVA_TYPE_MAP.put("varbinary", "byte[]");
    }
}
